package uk.ac.bbsrc.babraham.FastQC.Modules;

import java.awt.BorderLayout;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.TableModel;

import uk.ac.bbsrc.babraham.FastQC.Report.HTMLReportArchive;
import uk.ac.bbsrc.babraham.FastQC.Sequence.SequenceFactory;
import uk.ac.bbsrc.babraham.FastQC.Sequence.SequenceFile;
import uk.ac.bbsrc.babraham.FastQC.Sequence.Sequence;
import uk.ac.bbsrc.babraham.FastQC.Sequence.SequenceFormatException;

public class FilterOverRepresentedSeqsOptimized implements QCModule {
	protected HashMap<String, Integer> sequences = new HashMap<String, Integer>();
	protected HashMap<Integer, String> lines = new HashMap<Integer, String>();
	private String outputpath = new String();
	private String TempSeqsFilename = new String(); 
	private String GoodSeqsFilename = new String();
	private String BadSeqsFilename = new String();
	private File tempfile;
	private Writer tempoutput;
	private final String newline = new String("\n");
	private final String GoodSeqsFilePrefix = new String("Good_");
	private final String BadSeqsFilePrefix = new String("Bad_");
	private final String TempSeqsFilePrefix = new String("DeleteMe_");
	private boolean calculated = false;
	private int totalcount = 0;
	private final int HashKeyLength = 50;
	private final double PercentageLimit = 0.1;
	
	public String description() {
		return "Filter out sequences which are overrepresented in the set";
	}

	@Override
	public void processSequence(Sequence sequence) {
		Integer counter;
		
		calculated = false;
		++totalcount;
		
		if (GoodSeqsFilename.length() == 0) {
			try {
				outputpath = sequence.file().getFile().getCanonicalFile().getParent() + System.getProperty("file.separator");
				GoodSeqsFilename = outputpath + GoodSeqsFilePrefix + sequence.file().name();
				BadSeqsFilename  = outputpath + BadSeqsFilePrefix  + sequence.file().name();
				TempSeqsFilename = outputpath + TempSeqsFilePrefix + sequence.file().name();
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}		
			
			tempfile = new File(TempSeqsFilename);
			try {
				tempoutput = new BufferedWriter(new FileWriter(tempfile));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		String seq = sequence.getSequence();
		if (seq.length()>=HashKeyLength) {
			seq = new String(seq.substring(0, HashKeyLength));
		}
		
		if (sequences.containsKey(seq)) {
			counter = sequences.get(seq);	
		} else {
			// First occurrence of the sequence
			counter = 0;
		}
		++counter;
		sequences.put(seq, counter);
		
		lines.put(totalcount, seq);
		// Save the current sequence to temporary file
		saveSequenceToFile(sequence, tempoutput, 0.0, 0);
	}
	
	private void saveSequenceToFile(Sequence sequence, Writer output, Double percentage, Integer size) {
		try {
			output.write(sequence.getID() + newline);
			output.write(sequence.getSequence() + newline);
			output.write("+" + Double.toString(percentage) + "|" + Integer.toString(size) + newline);
			// output.write("+" + newline);
			output.write(sequence.getQualityString() + newline);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
	}
	
	private synchronized void getFilterOverrepresentedSeqs () throws IOException{
		// Identify over-represented sequences and save them into files
		
		SequenceFile tmpSeqsfile;
		
		// Open files for writing
		File badfile = new File(BadSeqsFilename);
		File goodfile = new File(GoodSeqsFilename);
		Writer badoutput = new BufferedWriter(new FileWriter(badfile));
		Writer goodoutput = new BufferedWriter(new FileWriter(goodfile));
		Writer filetowrite;
		String seq;
		Sequence sequence;
		Integer linecounter;
		Integer counter;
		// Close the temporary file and re-open for reading data
		tempoutput.close();
		// Open temporary file to read the sequences
		try {
			tmpSeqsfile = SequenceFactory.getSequenceFile(tempfile);
			Iterator<Integer> l = lines.keySet().iterator();

			while (l.hasNext()) {
				linecounter = l.next();
				seq = lines.get(linecounter);
				counter = sequences.get(seq);

				double percentage = ((double) counter / totalcount)*100;
				if (percentage > PercentageLimit) {
					// Bad sequences
					filetowrite = badoutput;
				} else {
					// Good sequences
					filetowrite = goodoutput;
				}
				// Read sequence from temporary file
				try {
					sequence = tmpSeqsfile.next();
					// Save to corresponding file
					saveSequenceToFile(sequence, filetowrite, percentage, counter);
				} catch (SequenceFormatException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}			
			}
			// Close and delete temporary file
			tmpSeqsfile.getFile().delete();
		} catch (SequenceFormatException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}		
		// Close files
		badoutput.close();
		goodoutput.close();
	}
	
	@Override
	public JPanel getResultsPanel() {
		JPanel returnPanel = new JPanel();
		returnPanel.setLayout(new BorderLayout());
		returnPanel.add(new JLabel("Filter out overrepresented sequences",JLabel.CENTER),BorderLayout.NORTH);
		
		if (!calculated)
			try {
				getFilterOverrepresentedSeqs();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		
		
		returnPanel.add(new JLabel("Data has been written to files in folder " + outputpath, JLabel.CENTER),BorderLayout.CENTER);

		
		return returnPanel;
	}

	@Override
	public String name() {
		return "Filter out overrepresented sequences";
	}

	@Override
	public void reset() {
		outputpath = new String();
		TempSeqsFilename = new String();
		GoodSeqsFilename = new String();
		BadSeqsFilename = new String();
		totalcount = 0;
		sequences.clear();
		lines.clear();
	}

	@Override
	public boolean raisesError() {
		// No error to be raised
		return false;
	}

	@Override
	public boolean raisesWarning() {
		// No warning to be raised
		return false;
	}

	@Override
	public boolean ignoreFilteredSequences() {
		// Do not apply filter
		return true;
	}

	@Override
	public void makeReport(HTMLReportArchive report) throws IOException {
		// When the main program calling this function, write data to file
		if (!calculated) getFilterOverrepresentedSeqs ();
		
	}
	

}
